<?php
/**
 * this class is part identity map and part unit of work pattern
 * it is based on the concepts from Matt Zandstra
 * 
 * The identity map concept is that if in your repository you call find() if the object is already instantiated it should
 * be returned instead of having to go through the creation process again. So it's kind of like an object cache
 * 
 * The unit of work concept is that any changes made to an object after it's created should be stored in a buffer and at the end
 * of the request or when the client says so, all the data should be actually saved, updated or deleted from the database. Instead of
 * having to call save() every time you make a change to the value of an object
 *
 */
class ObjectManager
{
	/*
	 * stores an array of instantiated objects for the identity map
	 * 
	 * @var array
	 */
	private $all 	= array();
	
	/**
	 * stores any objects that need to be updated
	 *
	 * @var array
	 */
	private $dirty 	= array();

	
	/**
	 * stores any objects that need to be inserted into the DB
	 *
	 * @var array
	 */
	private $new 	= array();
	
	
	private static $instance;
	
	public function __construct()
	{
		
	}
	
	static function instance() 
	{
		if( !self::$instance) {
			self::$instance = new ObjectManager();
		}
		return self::$instance; 
	}
	
	/**
	 * makes a key into based on classname and unique ID
	 *
	 * @param unknown_type $obj
	 * @return string
	 */
	function getKey($obj)
	{
		$key = get_class($obj).'.'.$obj->getId();
		return $key;
	}
	
	/**
	 * add an object to the identity map cache
	 *
	 * @param unknown_type $obj
	 */
	static function addToMap($obj) 
	{
		$inst = self::instance();
		$inst->all[$inst->getKey($obj)] = $obj;
	}
	
	/**
	 * check to see if an identity map entry exsists for this object
	 *
	 * @param unknown_type $obj
	 * @return unknown
	 */
	static function exists($classname, $id)
	{
		$inst = self::instance();
		$key = $classname.'.'.$id;
		if(array_key_exists($key, $inst->all)) {
			return $inst->all[$key];
		}
		return false;
	}
	
	
	static function markDirty($obj)
	{

		$inst = self::instance();
		if(empty($inst->new[$inst->getKey($obj)])) {
			$inst->dirty[$inst->getKey($obj)] = $obj;
		}
	}
	
	/**
	 * removes an object from the dirty array which means we've saved it ourselves
	 */
	static function markClean($obj)
	{
		$inst = self::instance();
		if(empty($inst->new[$inst->getKey($obj)])) {
			$inst->dirty[$inst->getKey($obj)] = $obj;
		}
	}
	
	/**
	 * actually does the work of writing the changes to the database
	 *
	 */
	static function performOperations()
	{
		$inst = self::instance();
		$messages = array();
		foreach($inst->dirty as $obj) {

			// check to make sure the object itself is valid as a whole
			if($obj->isValid()) {
				$obj->finder()->save($obj);
			} else {
				$messages = array_merge($messages, $obj->getMessages());
			}

		}
		$inst->dirty = array();
		return $messages;
		
	}
	
	function __destruct()
	{
		//$inst = self::instance();
		//$inst->performOperations();
	}
	
}
?>